# -*- coding: utf-8 -*-
from db.common import cli
import numpy as np
import pandas as pd
import warnings

"""
总体的评价维度选择包括如下：
1.端口利用率（主要考虑PON的分光路数不超过32或64）；1、2、3
2.PON口制式（考虑是GPON、EPON等，带宽为1.25G或1G、10G等）2、3
4.PON口全量用户数
6.高等级套餐用户数
8.高流量用户数量
9.PON口峰值流量
"""

"""
评价维度：节点流量瓶颈；设备物理承载压力；规划升级的潜在收益
"""

class AHP:
    # 初始化函数
    def __init__(self,criteria, b):
        #RI系数元组
        self.RI = (0, 0, 0.58, 0.9, 1.12, 1.24, 1.32, 1.41, 1.45, 1.49)
        #第二层两两比较的判定矩阵A
        self.criteria = criteria
        # b是一个list，其中list的元素个数为A的维度数n，每个元素是一个方案层维度数y的list。
        self.b = b
        # criteria的维度（第一个数据有几个元素）
        self.num_criteria = criteria.shape[0]
        # b的维度（第一个数据有几个元素）
        self.num_project = b[0].shape[0]

    # 计算相关前置系数的函数。input_matrix为第二层两两比较的数组（list）
    def cal_weights(self, input_matrix):
        # 数组格式标准化
        input_matrix = np.array(input_matrix)
        # 记录数组行数、列数
        n, n1 = input_matrix.shape

        # 如果数组格式不合理，进行报错
        assert n == n1, '不是一个方阵'
        for i in range(n):
            for j in range(n):
                if np.abs(input_matrix[i, j] * input_matrix[j, i] - 1) > 1e-7:
                    raise ValueError('不是反互对称矩阵')

        # eigenvalues存储第二层两两相互比较的矩阵的特征值（有整数、负数、复数等）
        # eigenvectors存储与eigenvalues每个值相对应的特征向量（有整数、负数、复数等）
        eigenvalues, eigenvectors = np.linalg.eig(input_matrix)

        # 取最大特征值的索引值（Index）
        max_idx = np.argmax(eigenvalues)
        # 取最大特征值的实数部分
        max_eigen = eigenvalues[max_idx].real
        # 取最大特征值对应的特征向量的实数部分
        eigen = eigenvectors[:, max_idx].real
        # 每个数除总和（归一化计算）
        eigen = eigen / eigen.sum()

        if n > 9:
            CR = None
            warnings.warn('无法判断一致性')
        else:
            CI = (max_eigen - n) / (n - 1)
            CR = CI / self.RI[n]
        if CR>0.1:
            print("CR超过0.1，一致性不可接受")
        # 返回最大特征值，CR和最大特征值对应的特征向量的实数部分
        return max_eigen, CR, eigen

    def run(self):
        # 获取第二层计算得到的最大特征值，CR和最大特征值对应的特征向量
        max_eigen, CR, criteria_eigen = self.cal_weights(self.criteria)
        # 打印是否检验通过
        print('准则层：最大特征值{:<5f},CR={:<5f},检验{}通过'.format(max_eigen, CR, '' if CR < 0.1 else '不'))
        print('准则层权重={}\n'.format(criteria_eigen))
        # 定义空list，用于打印呈现
        max_eigen_list, CR_list, eigen_list = [], [], []
        # 遍历b，计算每个b中每个矩阵的特征值、特征向量、CR等，添加进list
        for i in self.b:
            max_eigen, CR, eigen = self.cal_weights(i)
            max_eigen_list.append(max_eigen)
            CR_list.append(CR)
            eigen_list.append(eigen)

        # 表格打印
        pd_print = pd.DataFrame(eigen_list,
                                index=['准则' + str(i) for i in range(self.num_criteria)],
                                columns=['方案' + str(i) for i in range(self.num_project)],
                                )
        pd_print.loc[:, '最大特征值'] = max_eigen_list
        pd_print.loc[:, 'CR'] = CR_list
        pd_print.loc[:, '一致性检验'] = pd_print.loc[:, 'CR'] < 0.1
        print('方案层')
        print(pd_print)
        print(criteria_eigen)
        print(criteria_eigen.reshape(1, -1))
        print(np.array(eigen_list))
        # 目标层，np.dot为矩阵乘法；需要把两个list转换陈合适的矩阵相乘。
        obj = np.dot(criteria_eigen.reshape(1, -1), np.array(eigen_list))
        print('\n目标层', obj)
        print('最优选择是方案{}'.format(np.argmax(obj)))
        return str(np.abs(obj[0,0]))+","+str(np.abs(obj[0,1]))+","+str(np.abs(obj[0,2]))



def get_AHP_weights_impl():
    # PON口制式（1G或1.25G或10G）
    b1 = np.array([[1, 1 / 2, 1],
                   [2, 1, 2],
                   [1, 1 / 2, 1]])
    # 端口利用率（分光器分线路数）
    b2 = np.array([[1, 1 / 2, 2],
                   [2, 1, 4],
                   [1 / 2, 1 / 4, 1]])

    # PON口全量用户数
    b4 = np.array([[1, 1 / 3, 1 / 2],
                   [3, 1, 2],
                   [2, 1 / 2, 1]])

    # PON口高等级套餐用户数
    b6 = np.array([[1, 4, 1 / 2],
                   [1 / 4, 1, 1 / 6],
                   [2, 6, 1]])

    # PON口高流量用户数量
    b8 = np.array([[1, 2, 1 / 2],
                   [1 / 2, 1, 1 / 4],
                   [2, 4, 1]])
    # PON口峰值流量
    b9 = np.array([[1, 2, 3],
                   [1 / 2, 1, 2],
                   [1 / 3, 1 / 2, 1]])

    b = [b1, b2, b4, b6, b8, b9]

    criteria = np.array([[1, 1 / 2, 1 / 2, 1 / 3, 1 / 4, 1 / 5],
                         [2, 1, 1, 1, 1 / 2, 1 / 3],
                         [2, 1, 1, 1 / 2, 1 / 3, 1 / 3],
                         [3, 1, 2, 1, 1, 1 / 2],
                         [4, 2, 3, 1, 1, 1],
                         [5, 3, 3, 2, 1, 1]])
    a = AHP(criteria, b).run()
    return a

class AHP_TOPSIS:
    # 初始化函数
    def __init__(self,criteria, b):
        #RI系数元组
        self.RI = (0, 0, 0.58, 0.9, 1.12, 1.24, 1.32, 1.41, 1.45, 1.49)
        #第二层两两比较的判定矩阵A
        self.criteria = criteria
        # b是一个list，其中list的元素个数为A的维度数n，每个元素是一个方案层维度数y的list。
        self.b = b
        # criteria的维度（第一个数据有几个元素）
        self.num_criteria = criteria.shape[0]
        # b的维度（第一个数据有几个元素）
        self.num_project = b[0].shape[0]

    # 计算相关前置系数的函数。input_matrix为第二层两两比较的数组（list）
    def cal_weights(self, input_matrix):
        # 数组格式标准化
        input_matrix = np.array(input_matrix)
        # 记录数组行数、列数
        n, n1 = input_matrix.shape

        # 如果数组格式不合理，进行报错
        assert n == n1, '不是一个方阵'
        for i in range(n):
            for j in range(n):
                if np.abs(input_matrix[i, j] * input_matrix[j, i] - 1) > 1e-7:
                    raise ValueError('不是反互对称矩阵')

        # eigenvalues存储第二层两两相互比较的矩阵的特征值（有整数、负数、复数等）
        # eigenvectors存储与eigenvalues每个值相对应的特征向量（有整数、负数、复数等）
        eigenvalues, eigenvectors = np.linalg.eig(input_matrix)

        # 取最大特征值的索引值（Index）
        max_idx = np.argmax(eigenvalues)
        # 取最大特征值的实数部分
        max_eigen = eigenvalues[max_idx].real
        # 取最大特征值对应的特征向量的实数部分
        eigen = eigenvectors[:, max_idx].real
        # 每个数除总和（归一化计算）
        eigen = eigen / eigen.sum()

        if n > 9:
            CR = None
            warnings.warn('无法判断一致性')
        else:
            CI = (max_eigen - n) / (n - 1)
            CR = CI / self.RI[n]
        if CR>0.1:
            print("CR超过0.1，一致性不可接受")
        # 返回最大特征值，CR和最大特征值对应的特征向量的实数部分
        return max_eigen, CR, eigen

    def jisuan_lie_weight(self):
        #计算原始判定矩阵criteria每列的加和值
        lie_list = []
        # 数组格式标准化
        input_matrix = np.array(self.criteria)
        # 记录数组行数、列数
        n, n1 = input_matrix.shape
        # 计算每列的加和值
        for j in range(n):
            temp = 0
            for i in range(n):
                temp+=np.abs(input_matrix[i,j])
                if i == n-1 :
                    lie_list.append(temp)
                    temp = 0

        #得到指标归一化的B判定矩阵
        criteriaB = []
        for i in range(n):
            temp = []
            for j in range(n):
                temp.append(np.abs(input_matrix[i,j])/lie_list[j])
            criteriaB.append(temp)

        #计算行的权重向量
        hang_list = []
        input_matrixB = np.array(criteriaB)
        # 计算每行的加和值
        hang_total = 0
        for i in range(n):
            temp = 0
            for j in range(n):
                temp += np.abs(input_matrixB[i, j])
                if j == n - 1:
                    hang_list.append(temp)
                    hang_total+=temp
                    temp = 0


        #计算行归一向量，得到每个指标的具体权重值
        # print(hang_total)
        final_weight = []
        for i in range(n):
            final_weight.append(hang_list[i]/hang_total)
        return final_weight



    def run(self):
        data = self.jisuan_lie_weight()
        res = ""
        for i in range(len(data)):
            print(data[i])
            res+=str(data[i])
            if i != len(data) -1:
                res+=","
        print(data)
        return res
def get_TOPSIS_AHP_weights_impl():
    # 端口利用率（分光器分线路数）
    b1 = np.array([[1, 1 / 2, 2],
                   [2, 1, 4],
                   [2, 1 / 4, 1]])
    # PON口制式（1G或1.25G或10G）
    b2 = np.array([[1, 1 / 2, 1],
                   [2, 1, 2],
                   [1, 1 / 2, 1]])

    # PON口普通套餐用户数
    b3 = np.array([[1, 1 / 2, 1 / 3],
                   [2, 1, 1 / 2],
                   [3, 2, 1]])
    # PON口全量用户数
    b4 = np.array([[1, 1 / 3, 1 / 2],
                   [3, 1, 2],
                   [2, 1 / 2, 1]])
    # 流量均值
    b5 = np.array([[1, 3, 4],
                   [1 / 3, 1, 2],
                   [1 / 4, 1 / 2, 1]])

    # PON口高等级套餐用户数
    b6 = np.array([[1, 4, 1 / 2],
                   [1 / 4, 1, 1 / 6],
                   [2, 6, 1]])
    # PON流量增长趋势
    b7 = np.array([[1, 1, 4],
                   [1, 1, 4],
                   [1 / 4, 1 / 4, 1]])
    # PON口高流量用户数量
    b8 = np.array([[1, 2, 1 / 2],
                   [1 / 2, 1, 1 / 4],
                   [2, 4, 1]])
    # PON口峰值流量
    b9 = np.array([[1, 2, 3],
                   [1 / 2, 1, 1],
                   [1 / 3, 1, 1]])

    b = [b1, b2, b3, b4, b5, b6, b7, b8, b9]

    criteria = np.array([[1, 1, 1 / 2, 1 / 2, 1 / 3, 1 / 3, 1 / 4, 1 / 4, 1 / 5],
                         [1, 1, 1 / 2, 1 / 2, 1 / 3, 1 / 3, 1 / 3, 1 / 4, 1 / 5],
                         [2, 2, 1, 1 / 2, 1 / 2, 1 / 2, 1 / 3, 1 / 3, 1 / 3],
                         [2, 2, 2, 1, 1, 1 / 2, 1, 1 / 2, 1 / 2],
                         [3, 3, 2, 1, 1, 1, 1, 1 / 2, 1 / 3],
                         [3, 3, 2, 2, 1, 1, 1, 1 / 2, 1 / 3],
                         [4, 3, 3, 1, 1, 1, 1, 1 / 2, 1 / 3],
                         [4, 4, 3, 2, 2, 2, 2, 1, 1 / 2],
                         [5, 5, 3, 2, 3, 3, 3, 2, 1]])
    a = AHP_TOPSIS(criteria, b).run()
    return a